Repository navigation
Conversation
workflows - apps/web check fetches only the compared commit for its tree diff - server unit and integration shards check out depth 1 - changeset, snippet, image and migration checks fetch only to the merge base - retry the new fetches three times, as actions/checkout does - record the checkout footprint in every Build Check worker scripts - add fetch-merge-base.sh: doubling depth, exact merge base, full-history fallback tests - drive the fetch logic and the web change detector against file:// clones
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated paths require human/CODEOWNERS approval.
This PR modifies six .github/workflows/** files, which fall under the repo's hard, path-based approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so decision-table row 2 fires: the gate is not satisfied and the outcome must be escalate.
The reviewer findings are clean — CI-infrastructure-only changes that bound fetch-depth: 0 history fetches, with a new fail-closed scripts/fetch-merge-base.sh (MAX_DEPTH doubling with --unshallow fallback, shallow-boundary exactness guard, non-zero exit on failure) and strong regression test coverage (tests/ci-bounded-checkout.test.mjs). No protocol surface touched, so no changeset is required. No Critical/High/Medium findings.
The escalation is purely due to the unsatisfied gated-paths approval requirement. Once a human/CODEOWNERS approval lands (review_decision: APPROVED), this clean diff can fall through to approve.
Escalation reasons
- Modifies
.github/workflows/**(apps-web-check.yml, build-check.yml, changeset-check.yml, check-image-alt-text.yml, check-migration-numbers.yml, check-testable-snippets.yml) — files under the deterministic gated-paths approval gate; requires human/CODEOWNERS review (review_decision is REVIEW_REQUIRED, not APPROVED).
Why human review
- Modifies gated paths under
.github/workflows/**(6 workflow files) while review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required before merge. - This PR touches a path under a hard, non-overridable approval gate (.github/workflows/apps-web-check.yml (modified) matches
.github/workflows/**; .github/workflows/build-check.yml (modified) matches.github/workflows/**; .github/workflows/changeset-check.yml (modified) matches.github/workflows/**; .github/workflows/check-image-alt-text.yml (modified) matches.github/workflows/**; .github/workflows/check-migration-numbers.yml (modified) matches.github/workflows/**; .github/workflows/check-testable-snippets.yml (modified) matches.github/workflows/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
Closes #8559
Problem
Several verification lanes checked out with
fetch-depth: 0, which fetches every branch (about 2,070) and every tag before any check runs. #8559 shows the apps/web check spending its whole 10-minute budget in checkout, and a server unit shard spending 14m49s of 20 there. Most of these lanes only diff two snapshots or diff against the merge base; the server suites read no history at all.Changes
Audit
apps-web-check.ymlorigin/<base>(or the pushbeforecommit) against HEADbuild-check.ymlserver unit shardsgit rev-parse HEAD)build-check.ymlserver integration shardsblob:nonekept)changeset-check.ymlbase...HEAD,git show base:path,changeset status --sincecheck-testable-snippets.ymlbase...HEADnames and per-file diffscheck-image-alt-text.ymlmain...HEADto pick changed imagescheck-migration-numbers.ymlorigin/maintree andmain...HEADdeletionsbuild-check.ymlbuild workerbase...HEADplusrefs/tags/v<version>(a missing tag reads as unreleased)build-check.ymlcanonical shardsorigin/<stable>.x; withdrawn-release marker test checks real historytraining-agent-storyboards.ymlgit archive origin/3.0.xrelease.yml,release-docs.yml,recover-protocol-cdn.yml,deploy.yml,forward-merge-3.*.ymlai-review.ymlThere are no composite actions under
.github/actions/.scripts/fetch-merge-base.shFetches commits and trees (no blobs) for
origin/<base>and HEAD with a doubling depth (50 up to 3,200) untilorigin/<base>...HEADhas an exact merge base. Then it falls back to the complete history of those two refs, and exits non-zero if there is still no merge base. Two details matter:It prints the merge base, the depth that found it, the elapsed time and
git count-objects -v. Each fetch is tried 3 times, 5 s apart, asactions/checkoutdoes for its own fetches. If the last attempt fails, orgit merge-baseorgit rev-listerrors, it exits with::error::..github/workflows/apps-web-check.ymlDepth-1 checkout, then a depth-1 fetch of the compared commit (3 attempts). The tree diff is unchanged. If that commit still cannot be fetched, or the diff errors, every web check runs with a warning. The stable required context is unchanged.
.github/workflows/build-check.ymlServer unit and integration shards check out depth 1. Every worker gets a "Report checkout footprint" step, so CI shows what each checkout fetched next to its duration. The build worker and canonical shards keep full history, with comments saying why.
Merge-base lanes
changeset-check.yml,check-testable-snippets.yml,check-image-alt-text.ymlandcheck-migration-numbers.ymlrun the script before diffing.scripts/fetch-merge-base.sh, and the image lane on its own workflow file, so this PR's CI exercises them.changeset-check.ymltakes the base ref from a job-levelBASE_REFinstead of interpolating${{ github.base_ref }}intorun:.Tests
tests/ci-bounded-checkout.test.mjs(22 tests, run bytest:unit) runs the script and the real apps/web detector step against file:// shallow clones of a fixture repo. A git stub onPATHinjects failures. Each fix fails its test when reverted. It covers:rev-list, and fetch retries (recovery and exhaustion);--name-only,--name-status --find-renames) match full history in all 50 comparisons, including chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates #7388, whose merge base is 1,039 main commits back.blob:nonetook 1.8 s to fetch and 462 s to check out, because the 1.3 GiB tip arrives through lazy blob fetches. Depth 1 took 32 s plus 61 s (one pack). Local timings do not reproduce GitHub latency; the new footprint steps are there to measure fresh runners.test-coverage-registration,changeset-protocol-scopeandrelease-workflow-immutabilitypass.Decisions for reviewers
blob:noneplus the sparse set, so about 0.8 GiB of blobs still arrive through lazy fetches. Locally, that path dominated full-tree checkout time. Watch the new footprint step, and switch to a plain depth-1 fetch if it stays slow.dist/protocol/tarballs none of them read).filter: blob:noneplus a!/dist/protocol/sparse set is a likely bigger win, left as a follow-up.Out of scope / follow-ups
origin/<stable>.xand real history for two tests.training-agent-storyboards.ymlappears to need only theorigin/3.0.xtip, but it is a protocol-scoped workflow.blob:noneplus a sparse set withoutdist/protocol/(decision 4).🤖 Generated with Claude Code